<?php

namespace app\common\validate;

use app\common\exception\ExceptionEmun;
use app\common\exception\ParameterException;
use app\common\exception\SystemException;
use Doctrine\Common\Annotations\AnnotationReader;
use hg\apidoc\annotation\Param;
use hg\apidoc\ParseAnnotation;
use think\Validate;

/**
 * Class BaseValidate
 * 验证类的基类
 */
class BaseValidate extends Validate
{

    use ParseAnnotation;

    /**
     * 输出异常时附带的值
     * @var array
     */
    protected $rule = [];
    /**
     * 抛出异常需要夹带的数据
     * @var array
     */
    protected $exception_data = [];
    /**
     * 校验不通过的字段名
     * @var string
     */
    protected $error_field = '';

    /**
     * 场景校验规则
     * 建议覆盖
     * @var array[]
     */
    protected $sceneRule = [
//        'example' => [
//            'only' => [
//                'username', 'password', 'isRemember'
//            ],
//            'append' => [
//                'username' => 'unique:user'
//            ],
//            'remove' => [],
//        ],
    ];

    /**
     * BaseValidate constructor.
     * @param array $rules
     * @param array $message
     * @param array $field
     */
    public function __construct(array $rules = [], array $message = [], array $field = [])
    {
        parent::__construct($rules, $message, $field);
    }

    /**
     * 获取返回异常的data
     * @return array
     */
    public function getExceptionData()
    {
        return $this->exception_data;
    }

    /**
     * 设置注解校验,读取注解并写入sceneRule
     * @param string $class
     * @param string $scene
     * @return BaseValidate
     * @throws \ReflectionException
     */
    public function setAnnotateChekck(string $class, string $scene)
    {
        if (empty($this->sceneRule[$scene])) {
            $this->sceneRule[$scene]= [
                'only' => [],
                'append' => [],
                'remove' => [],
            ];
        }

        $theSceneRule = &$this->sceneRule[$scene];
        $append = &$theSceneRule['append'];

        $reflection_method = new \ReflectionMethod($class, $scene);
        if (!empty($reflection_method)) {
            $reader = new AnnotationReader();
            $annotations = $reader->getMethodAnnotations($reflection_method);
            foreach ($annotations as $annotation) {
                switch (TRUE) {
                    case $annotation instanceof Param:
                        $type = explode("|", $annotation->type);
                        $require = $annotation->require;
                        $desc = $annotation->desc;
                        $name = $annotation->value;
                        if (!empty($append[$name])) {
                            //不敢原本的是字符串还是数组,全部转换为数组处理
                            $append[$name] = is_array($append[$name]) ? $append[$name] : [$append[$name]];
                            $append[$name] = array_merge($append[$name], $type);
                        } else {
                            $append[$name] = $type;
                        }
                        if (!empty($require)) {
                            array_unshift($append[$name], 'require');
                        }

                }
            }
        }
        return $this;
    }

    /**
     * 校验数据的前置工作,这里是把append加入only
     * @param array $data
     * @return bool
     */
    protected function beforRunCheck($data, $rules = '', $scene = '')
    {
//      把append加入only
        if (!empty($this->sceneRule[$scene])&&!empty($the_scene['append'])) {
            $the_scene = $this->sceneRule[$scene];
            $append_keys = array_keys($the_scene['append']);
            $the_scene['only'] = !empty($the_scene['only']) ? $the_scene['only'] : [];
            $the_scene['only'] = array_merge($append_keys, $the_scene['only']);
            $this->sceneRule[$scene] = $the_scene;
        }
        return TRUE;
    }

    /**
     * 自定义检测验证类规则
     * @param $data
     * @param string $scene
     * @param string $msg 可以不填,不填使用TP的提示语
     * @param array $ret_data 可以不填,不填使用默认异常返回数据
     * @return bool
     * @throws BaseException
     */
    public function runCheck($data, $rules = '', $scene = '')
    {
        if ($this->beforRunCheck($data,$rules,$scene)) {
            $this->scene($scene);
            $check_result = $this->check($data, $this->rule, $scene);
            if (FALSE === $check_result) {
                $this->throwException($data);
                return TRUE;
            }
        }
        return TRUE;
    }

    /**
     * 抛出异常
     * @param $data
     */
    protected function throwException($data)
    {
        if ($this->beforExcetion($data)) {
            ExceptionEmun::throwException(
                SystemException::INVALID_PARAMETER,
                $this->error, $this->exception_data);
        }
        return TRUE;
    }


    /**
     * 抛出错误前的处理函数,这里是把错误字段加入异常数组
     * @param $data
     */
    protected function beforExcetion($data)
    {
        $this->exception_data = array_merge(
            $this->exception_data,
            ['validate_error_field' => $this->getErrorField()]
        );
        return TRUE;
    }

    /**
     * 验证单个字段规则
     * 重写覆盖父方法,保存error_field
     * @access protected
     * @param string $field 字段名
     * @param mixed $value 字段值
     * @param mixed $rules 验证规则
     * @param array $data 数据
     * @param string $title 字段描述
     * @param array $msg 提示信息
     * @return mixed
     */
    protected function checkItem($field, $value, $rules, $data, $title = '', $msg = [])
    {
        $ret = parent::checkItem($field, $value, $rules, $data, $title, $msg);
        if (!empty($ret) && TRUE !== $ret) {
            $this->error_field = $field;
        }
        return $ret;
    }

    /**
     * 获取校验结果错误的字段
     * @return string
     */
    public function getErrorField(): string
    {
        return $this->error_field;
    }

    /**
     * 加载自定义场景
     * loadScene
     */
    public function loadScene($scene_name)
    {
        if (!empty($this->sceneRule[$scene_name])) {
            $scene = $this->sceneRule[$scene_name];
            if (!empty($scene['append'])) {

                $this->append($scene['append']);
            }
            if (!empty($scene['only'])) {
                $this->only($scene['only']);
            }
            if (!empty($scene['remove'])) {
                $this->remove($scene['remove']);
            }
        }
    }

    /**
     * 获取数据验证的场景,
     * 覆盖掉父方法,加入loadScene
     * @param string $scene_name
     * @return array
     */
    protected function getScene(string $scene): void
    {
        //会清空only,append,remove
        parent::getScene($scene);
        //兼容原场景
        if (empty($this->scene[$scene])) {
            //加载自定义场景
            $this->loadScene($scene);
        }

    }

    /**
     * 正整数检测
     * @param $value
     * @param string $rule
     * @param string $data
     * @param string $field
     * @return bool|string
     */
    protected function isPositiveInteger($value, $rule = '', $data = [], $field = '', $describe)
    {
        if (is_positive_int($value)) {
            return TRUE;
        }
        $this->error_field = $field;
        return $describe . '必须是正整数';
    }

    /**
     * 非空检测
     * @param $value
     * @param string $rule
     * @param string $data
     * @param string $field
     * @return bool|string
     */
    protected function isNotEmpty($value, $rule = '', $data = [], $field = '', $describe)
    {
        if (empty($value)) {
            $this->error_field = $field;
            return $describe . '不允许为空';
        } else {
            return TRUE;
        }
    }

    /**
     * 字符串非空检测,数组包含空字符串也不行
     * @param $value
     * @param string $rule
     * @param string $data
     * @param string $field
     * @return bool|string
     */
    protected function isNotEmptyString($value, $rule = '', $data = [], $field = '', $describe)
    {
        if (is_empty_string($value)) {
            $this->error_field = $field;
            return $describe . '不允许为空';
        } else {
            return TRUE;
        }
    }

    /**
     * 字符串校验
     * @param $value
     * @param string $rule
     * @param array $data
     * @param string $field
     * @param $describe
     * @return bool|string
     */
    protected function string($value, $rule = '', $data = [], $field = '', $describe)
    {
        if (is_string($value)) {
            return TRUE;
        } else {
            $this->error_field = $field;
            return $describe . "不是字符串";
        }
    }

    /**
     * 手机号检测
     * @param $value
     * @param string $rule
     * @param string $data
     * @param string $field
     * @return bool|string
     */
    protected function isMobile($value, $rule = '', $data = [], $field = '', $describe)
    {
        $prg_rule = '^1\d{10}$^';
        $result = preg_match($prg_rule, $value);
        if ($result) {
            return TRUE;
        } else {
            $this->error_field = $field;
            return $describe."不是手机号";
        }
    }
}
